package cz.acies.uniql.sql;

import java.util.*;

import org.dom4j.Element;
import org.dom4j.dom.DOMElement;


import cz.acies.uniql.dialect.*;
import cz.acies.uniql.metadata.DBMetaData;
import cz.acies.uniql.sql.element.*;

public class DeleteQuery extends Query
{
	private static final long serialVersionUID = 8465277448726081150L;

	private List<UniQLExpr> deleteFrom = new ArrayList<UniQLExpr>();

	private ArrayList<UniQLExpr> tables;
	private ArrayList<UniQLExpr> joins;
	private UniQLWhere where;
	
	// -----  -----------------------------------------------------------------

	/**
	 * Konstruktor tridy DeleteQuery.
	 */
	public DeleteQuery() {
		super();
	}

	public DeleteQuery(DBMetaData dbmd) {
		super(dbmd.getDialect(), dbmd);
	}

	/**
	 * Konstruktor tridy DeleteQuery.
	 *
	 * @param dialect - dialect
	 * @param dbmd - dbmd
	 */
	public DeleteQuery(Dialect dialect, DBMetaData dbmd) {
		super(dialect, dbmd);
	}

	/**
	 * Konstruktor vytvori nonou instanci objektu DeleteQuery s vlastnostmi
	 * objektu zadaneho jako parametr konstruktoru.
	 *  
	 * @param orderBy
	 */
	public DeleteQuery(DeleteQuery query) {
		this();
		DeleteQuery clone = (DeleteQuery)query.clone();
		
		setId(clone.getId());
		setName(clone.getName());
		setDialect(clone.getDialect());
		setDBMetaData(clone.dbmd());
		setAliasMap(clone.getAliasMap());

		setDialect(clone.getDialect());
		setDeleteFrom(clone.getDeleteFrom());
		setTables(clone.getTables());
		setJoins(clone.getJoins());
		setAliasMap(clone.getAliasMap());
		setWhere(clone.getWhere());
	}

	// -----  -----------------------------------------------------------------

	public String translate() throws Exception {
		StringBuffer sb = new StringBuffer("");
		Dialect dialect = this.getDialect();
		sb.append("DELETE ");
		if (this.getDeleteFrom() != null && !this.getDeleteFrom().isEmpty()) {
			for (Iterator<UniQLExpr> iterator = deleteFrom.iterator(); iterator.hasNext();) {
				UniQLFrom from = (UniQLFrom) iterator.next();
				from.setUniQLProperties(this);
				String fromName = from.getName(); 
				sb.append(dialect.LQ() + fromName + dialect.RQ());
				sb.append((iterator.hasNext()) ? ", " : " ");
			}
		}
		if (this.getTables() != null) {
			sb.append("FROM ");
			for (Iterator<UniQLExpr> i = getTables().iterator(); i.hasNext();) {
				UniQLExpr table = (UniQLExpr)i.next();
				table.setUniQLProperties(this);
				String tableName = table.getName();
				if (getDialect().isKeyword(tableName.toUpperCase())) {
					tableName = getDialect().getKeywordAlter(tableName.toUpperCase());
					if (tableName != null) table.setName(tableName);
				}
				sb.append(table.translate());
				if (i.hasNext()) sb.append(", ");
	        }
		}
		if (dialect.isUpdateJoinWithFrom()) {
			if (this.getJoins() != null && !this.getJoins().isEmpty()) {
				UniQLExpr from = (UniQLExpr)this.getTables().get(0);
				if (from != null) {
					from.setUniQLProperties(this);
					sb.append("\nFROM "+from.translate()+" ");
					for (Iterator<UniQLExpr> i = getJoins().iterator(); i.hasNext();) {
						UniQLExpr join =  i.next();
						join.setUniQLProperties(this);
						((UniQLJoin) join).setAliasMap(getAliasMap());
						sb.append("\n" + join.translate());
					}
				}
			}
		} else {
			if (this.getJoins() != null) {
				for (Iterator<UniQLExpr> i = getJoins().iterator(); i.hasNext();) {
					UniQLExpr join = i.next();
					join.setUniQLProperties(this);
					sb.append("\n"+join.translate());
		        }	
			}
		}
		if (this.getWhere() != null && !this.getWhere().isEmpty()) {
			where.setUniQLProperties(this);
			sb.append("\n"+this.getWhere().translate());
		}
		return sb.toString();
	}
	
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@SuppressWarnings("unchecked")
	public Object clone() {
		DeleteQuery sql = new DeleteQuery();
		try {
			sql.setDialect(this.getDialect());
			sql.setDeleteFrom((ArrayList<UniQLExpr>)((ArrayList<UniQLExpr>)getDeleteFrom()).clone());
			sql.setTables((ArrayList<UniQLExpr>) this.getTables().clone());
			sql.setJoins((ArrayList<UniQLExpr>) this.getJoins().clone());
			sql.setAliasMap((HashMap<String, Object>) this.getAliasMap().clone());
			sql.setWhere((this.getWhere() != null) ? (UniQLWhere)this.getWhere().clone() : null);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sql;
	}

	public DeleteQuery clear() {
		if (tables != null) tables.clear(); tables = null;
		if (joins != null) joins.clear(); 	joins = null;
		if (where != null) where.clear();  	where = null;
		if (deleteFrom != null) {
			deleteFrom.clear();
		}
		return this;
	}
	
	// -----  ------------------  ---------------------------------------------

	/**
	 * Metoda p�id� do dotazu klauzuli Where
	 * @param 	where
	 * @return	DeleteQuery
	 */
	public DeleteQuery setWhere(UniQLWhere where) {
		this.where = where;
		return this;
	}

	/**
	 * Metoda p�id� do dotazu do klauzule UniQLWhere obsah parametru UniQLWhere
	 * @param 	where
	 * @return	UpdateQuery
	 */
	public DeleteQuery addWhere(UniQLWhere where) {
		if (this.where == null || this.where.isEmpty()) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(where.getCriterions());
		return this;
	}

	/**
	 * Metoda p�id� do Where klauzule dotazu podm�nku 
	 * @param cf	Parametr typu UniQLCondition
	 * @return		DeleteQuery
	 */	
	public DeleteQuery addWhereCondition(UniQLCondition cf) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCondition(cf);
		return this;
	}

	/**
	 * To sam� co metoda addWhereCondition(UniQLCondition cf)
	 * 
	 * @param cf - cf
	 * @return vraci delete query
	 */
	public DeleteQuery add(UniQLCondition cf) {
		return addWhereCondition(cf);
	}

	/**
	 * Metoda p�id� do Where klauzule dotazu podm�nku a oper�tor AND nebo OR, 
	 * kter�m bude podm�nka navazovat na dal�� podm�nku. Pokud dal�� podm�nka 
	 * neexistuje, je oper�tor ignorov�n.
	 * 
	 * @param cf 	Parametr typu UniQLCondition 
	 * @param op	�et�zcov� promenn� mus� m�t hodnotu AND nebo OR
	 * @return		DeleteQuery
	 */		
	public DeleteQuery addWhereCondition(UniQLCondition cf, String op) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCondition(cf, op);
		return this;
	}

	/**
	 * To sam� co metoda addWhereCondition(UniQLCondition cf, String op).
	 * 
	 * @param cf - cf
	 * @param op - op
	 * @return vraci delete query
	 */
	public DeleteQuery add(UniQLCondition cf, String op) {
		return addWhereCondition(cf, op);
	}

	/**
	 * Metoda prida do UniQLWhere klauzuli dotazu podminku a operator AND nebo OR, 
	 * ktery bude vlozen mezi predchozi a pridavanou podminku. Pokud predchozi 
	 * podminka neexistuje, je operator ignorovan.
	 * 
	 * @param cf Parametr typu UniQLCondition
	 * @param op retezcova promenna musi mit hodnotu AND nebo OR
	 * 
	 * @return objekt DeleteQuery
	 */
	public DeleteQuery addWhereCondition(String op, UniQLCondition cf) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.setLastConditionOperand(op);
		this.where.addCondition(cf);
		return this;
	}

	/**
	 * Metoda schodn�a s addWhereCondition(op, cf)
	 * 
	 * @param op
	 * @param cf
	 * @return objrct DeleteQuery
	 */
	public DeleteQuery add(String op, UniQLCondition cf) {
		return addWhereCondition(op, cf);
	}

	/**
	 * Metoda prida do UniQLWhere klauzuli dotazu podminku a operator1 AND nebo OR, 
	 * ktery bude vlozen mezi predchozi a pridavanou podminku a na ni nastaven operator2. 
	 * Pokud predchozi podminka neexistuje, je operator1 ignorovan.
	 * 
	 * @param op1 retezcova promenna musi mit hodnotu AND nebo OR
	 * @param cf Parametr typu UniQLCondition
	 * @param op2 retezcova promenna musi mit hodnotu AND nebo OR
	 * 
	 * @return objekt DeleteQuery
	 */
	public DeleteQuery addWhereCondition(String op1, UniQLCondition cf, String op2) {
		if (this.where == null) {			
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		where.setLastConditionOperand(op1);
		where.add(cf, op2);
		return this;
	}

	/**
	 * Metoda schodna s addWhereCondition(op1, cf, op2)
	 * 
	 * @param op
	 * @param cf
	 * @return object DeleteQuery
	 */
	public DeleteQuery add(String op1, UniQLCondition cf, String op2) {
		return addWhereCondition(op1, cf, op2);
	}

	/**
	 * P�id� ke stavajicim podm�nk�m WHERE klausule nov�, z obsa�en� v parametru criterions.
	 * 
	 * @param c
	 * @return
	 */
	public DeleteQuery addWhereCriterion(UniQLCriterion c) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(c);
		return this;
	}

	/**
	 * Metoda schodn� s addWhereCriterion(c) 
	 * @param c
	 * @return
	 */
	public DeleteQuery add(UniQLCriterion c) {
		return addWhereCriterion(c);
	}

	/**
	 * Metoda nejprve nastav� LastConditionOperator na parametr operator ve st�vj�c�m 
	 * seznamu podm�nek a pak p�id� dal�� podm�nky odsa�en� s objectu UniQLCrierion.
	 * 
	 * @param op
	 * @param c
	 * @return
	 */
	public DeleteQuery addWhereCriterion(String op, UniQLCriterion c) {
		if (this.where == null) {
			this.where = new UniQLWhere();
			this.where.setUniQLProperties(this);
		}
		this.where.addCriterions(op, c);
		return this;
	}

	/**
	 * Metoda schodn� s addWhereCriterion(op, c) 
	 * @param c
	 * @return
	 */
	public DeleteQuery add(String op, UniQLCriterion c) {
		return addWhereCriterion(op, c);
	}

	/**
	 * Navraci objekt where.
	 * 
	 * @return - vraci hodnotu where
	 */
	public UniQLWhere getWhere() {
		return this.where;
	}
	
	/**
	 * Vraci true, pokud je aktivni objekt where.
	 * 
	 * @return - vraci true, pokud je aktivni objekt where.
	 */
	public boolean hasWhere() {
		if (this.where == null) return false;
		if (this.where.isEmpty()) return false;
		return true;
	}

	/**
	 * Odstrani objekt where.
	 */
	public void removeWhere() {
		this.where = null;
	}

	/**/

	/**
	 * Prida from.
	 * 
	 * @param tableName - table name
	 * @return vraci delete query
	 */
	public DeleteQuery addFrom(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		return this.addFrom(from);
	}

	/**
	 * Prida from.
	 * 
	 * @param tableName - table name
	 * @param alias - alias
	 * @return vraci delete query
	 */
	public DeleteQuery addFrom(String tableName, String alias) {
		UniQLFrom from = new UniQLFrom(tableName, alias);
		return this.addFrom(from);
	}
	
	/**
	 * Prida from.
	 * 
	 * @param from - from
	 * @return vraci delete query
	 */
	public DeleteQuery addFrom(UniQLFrom from) {
		from.setUniQLProperties(this); 
		getTables().add(from);
		return this;
	}
		
	/**
	 * Zadava hodnotu from.
	 * 
	 * @param tableName - table name
	 * @return vraci delete query
	 */
	public DeleteQuery setFrom(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		return this.setFrom(from);
	}

	/**
	 * Zadava hodnotu from.
	 * 
	 * @param tableName - table name
	 * @param alias - alias
	 * @return vraci delete query
	 */
	public DeleteQuery setFrom(String tableName, String alias) {
		UniQLFrom from = new UniQLFrom(tableName, alias);
		return this.setFrom(from);
	}
	
	/**
	 * Zadava hodnotu from.
	 * 
	 * @param from - from
	 * @return vraci delete query
	 */
	public DeleteQuery setFrom(UniQLFrom from) {
		getTables().clear();
		from.setUniQLProperties(this);
		getTables().add(from);
		return this;
	}

	/**/
	
	/**
	 * Metoda Prida do seznamu tabulek, ze kter�ch budou z�znamy maz�ny dal�� 
	 * tabulku.
	 * 
	 * @param tableName - table name
	 * @return vraci delete query
	 */
	public DeleteQuery addDeleteFrom(String tableName) {
		UniQLFrom from = new UniQLFrom(tableName);
		return this.addDeleteFrom(from);
	}

	/**
	 * Metoda Prida do seznamu tabulek, ze kter�ch budou z�znamy maz�ny dal�� 
	 * tabulku.
	 *  
	 * @param from - from
	 * @return vraci delete query
	 */
	public DeleteQuery addDeleteFrom(UniQLFrom from) {
		from.setUniQLProperties(this);
		getDeleteFrom().add(from);
		return this;
	}

	/**/
	
	public DeleteQuery addJoin(UniQLJoin join) {
		if (this.joins == null) this.joins = new ArrayList<UniQLExpr>();
		if (join != null && join instanceof UniQLJoin) {
			this.joins.add(join);
		}
		return this;
	}

	public DeleteQuery setJoin(UniQLJoin join) {
		if (this.joins == null) this.joins = new ArrayList<UniQLExpr>();
		if (join != null && join instanceof UniQLJoin) {
			this.joins.clear();
			this.joins.add(join);
		}
		return this;
	}
	
	public ArrayList<UniQLExpr> getJoins() {
		if (joins == null) {
			joins = new ArrayList<UniQLExpr>();
		}
		return joins;
	}

	/**
	 * Nahradi stavajici kontejner joins za novy.
	 * 
	 * @param novy kontejner joins
	 */
	public void setJoins(ArrayList<UniQLExpr> joins) {
		this.joins = joins;
	}

	// -----  -----------------------------------------------------------------

	/**
	 * Nahradi stavajici kontejner tables za novy.
	 * 
	 * @param novy kontejner tables
	 */
	public void setTables(ArrayList<UniQLExpr> tables) {
		this.tables = tables;
	}

	public ArrayList<UniQLExpr> getTables() {
		if (tables == null) {
			tables = new ArrayList<UniQLExpr>();
		}
		return tables;
	}

	/**
	 * Metoda vrac� seznam tabulek ze kter�ch budou z�znamy maz�ny
	 *  
	 * @return the deleteFrom
	 */
	public List<UniQLExpr> getDeleteFrom() {
		return deleteFrom;
	}

	/**
	 * Metoda vlo�� seznam tabulek ze, kter�ch budou z�znamy maz�ny
	 * @param deleteFrom the deleteFrom to set
	 */
	public void setDeleteFrom(List<UniQLExpr> deleteFrom) {
		this.deleteFrom = deleteFrom;
	}

	// -----  -----------------------------------------------------------------
	
	public String toSQLString() throws Exception {
		return this.translate();
	}
	
	// -----  -----------------------------------------------------------------
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML()
	 */
	public String transToXML() throws Exception {
		DOMElement node = new DOMElement("SelectQuery");
		transToXML(node);
		return node.asXML();
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#transToXML(org.dom4j.dom.DOMElement)
	 */
	public DOMElement transToXML(DOMElement node) throws Exception {
		node.setName("SelectQuery");
		if (this.getTables() != null) {
			DOMElement child = new DOMElement("tables");
			for (Iterator<UniQLExpr> i = getTables().iterator(); i.hasNext();) {
				UniQLExpr table = i.next();
				child.appendChild(table.transToXML(new DOMElement("UniQLExpr")));
			}
			node.appendChild(child);
		}
		if (this.getJoins() != null) {
			DOMElement child = new DOMElement("joins");
			for (Iterator<UniQLExpr> i = getJoins().iterator(); i.hasNext();) {
				UniQLExpr join = i.next();
				child.appendChild(join.transToXML(new DOMElement("UniQLExpr")));
	        }
			node.appendChild(child);
		}
		if (this.getWhere() != null) {
			DOMElement child = this.where.transToXML(new DOMElement("UniQLWhere"));
			node.appendChild(child);
		}
		return node;
	}
	
	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(java.lang.String)
	 */
	public UniQLExpr xmlToExpr(String xml) throws Exception {
		System.out.println("SelectQuera.xmlToExpr(xml)");
		return null;
	}

	/* (non-Javadoc)
	 * @see cz.acies.uniql.sql.UniQLExpr#xmlToExpr(org.dom4j.Element)
	 */
	@SuppressWarnings("unchecked")
	public UniQLExpr xmlToExpr(Element node) throws Exception {
		ArrayList<Element> sections = new ArrayList<Element>(node.elements());
		for (int i = 0; i < sections.size(); i++) {
			Element section = (Element)sections.get(i);
			String nodename = section.getName();
			if (nodename.equals("tables")) {
				ArrayList<Element> list = new ArrayList<Element>(section.elements());
				for (int j = 0; j < list.size(); j++) {
					UniQLExpr expr = null;
					Element tblnode = (Element)list.get(j);
					expr = new UniQLFrom().xmlToExpr(tblnode);
					if (expr != null) this.addFrom((UniQLFrom)expr);
				}
			} else if (nodename.equals("joins")) {
				ArrayList<Element> list = new ArrayList<Element>(section.elements());
				for (int j = 0; j < list.size(); j++) {
					UniQLExpr expr = null;
					Element joinnode = (Element)list.get(j);
					expr = new UniQLJoin().xmlToExpr(joinnode);
					if (expr != null) addJoin((UniQLJoin)expr);
				}
			} else if (nodename.equals("UniQLWhere")) {
				UniQLExpr expr = null;
				expr = new UniQLWhere().xmlToExpr(section);
				if (expr != null) addWhere((UniQLWhere)expr);
			}
		}
		return this;
	}
}